﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using BrainTechLLC.ThreadSafeObjects;
using System.Collections.ObjectModel;

namespace BrainTechLLC
{
    public static class ListAndArrayExtensions
    {
        public static bool Remove<T>(this ObservableCollection<T> collection, bool onlyFirst, Func<T, bool> test) where T : class
        {
            bool found = false;

            for (int n = collection.Count - 1; n >= 0; n--)
            {
                if (test(collection[n]))
                {
                    found = true;

                    collection.RemoveAt(n);

                    if (onlyFirst)
                        break;
                }
            }

            return found;
        }

        public static List<TClass> ConvertList<TInterface, TClass>(this List<TInterface> list)
            where TInterface : class
            where TClass : class, TInterface
        {
            List<TClass> returnList = new List<TClass>(list.Count);
            for (int n = 0; n < list.Count; n++) returnList.Add(list[n] as TClass);
            return returnList;
        }

        public static List<TClass> CreateList<TInterface, TClass>(this TInterface[] items)
            where TInterface : class
            where TClass : class, TInterface
        {
            List<TClass> returnList = new List<TClass>(items.Length);
            for (int n = 0; n < items.Length; n++) returnList.Add(items[n] as TClass);
            return returnList;
        }

        public static long MaxID<T>(this List<T> items)
             where T : class, IProvidesUniqueID
        {
            long max = 0;

            foreach (T item in items)
            {
                if (item.UniqueID > max)
                    max = item.UniqueID;
            }

            return max;
        }

        public static TClass[] ToArray<TInterface, TClass>(this List<TInterface> list)
            where TInterface : class
            where TClass : class, TInterface
        {
            TClass[] array = new TClass[list.Count];
            for (int n = 0; n < list.Count; n++) array[n] = list[n] as TClass;
            return array;
        }

        public static TClass[] ToArray<TInterface, TClass>(this TInterface[] items)
            where TInterface : class
            where TClass : class, TInterface
        {
            TClass[] array = new TClass[items.Length];
            for (int n = 0; n < items.Length; n++) array[n] = items[n] as TClass;
            return array;
        }

        public static TInterface[] ToInterfaceArray<TInterface, TClass>(this TClass[] items)
            where TInterface : class
            where TClass : class, TInterface
        {
            TInterface[] array = new TInterface[items.Length];
            for (int n = 0; n < items.Length; n++) array[n] = items[n] as TInterface;
            return array;
        }

        public static List<T> Apply<T>(this List<T> list, Action<T> del)
             where T : class
        {
            for (int n = 0; n < list.Count; n++)
            {
                T entry = list[n];
                del(entry);
            }

            return list;
        }

        public static bool CompareCollections<T1>(this List<T1> newCollection, List<T1> oldCollection, Func<T1, int> getKey, out List<T1> newItems, out List<T1> noLongerPresent)
        {
            return CompareCollections<T1, T1>(newCollection, oldCollection, getKey, getKey, out newItems, out noLongerPresent);
        }

        public static bool CompareCollections<T1, T2>(this List<T1> newCollection, List<T2> oldCollection, Func<T1, int> getKeyT1, Func<T2, int> getKeyT2, out List<T1> newItems, out List<T2> noLongerPresent)
        {
            List<int> handled = new List<int>();
            newItems = new List<T1>();
            noLongerPresent = new List<T2>();

            foreach (var item1 in newCollection)
            {
                int id1 = getKeyT1(item1);
                bool found = false;

                foreach (var item2 in oldCollection)
                {
                    int id2 = getKeyT2(item2);
                    if (id1 == id2)
                    {
                        found = true;
                        break;
                    }
                }

                if (!found)
                    newItems.Add(item1);

                handled.Add(id1);
            }

            if (oldCollection != null)
            {
                for (int n = oldCollection.Count - 1; n >= 0; n--)
                {
                    var item2 = oldCollection[n];
                    int id2 = getKeyT2(item2);
                    bool found = false;

                    foreach (var id in handled)
                    {
                        if (id == id2)
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                        noLongerPresent.Add(item2);
                }
            }

            return (newItems.Count == 0 && noLongerPresent.Count == 0);
        }


        public static string BuildList<T>(this List<T> items, StringBuilder sbReused, string delimiter, Func<T, string> del)
        {
            sbReused.Length = 0;
            if (items == null) return string.Empty;

            for (int n = 0; n < items.Count; n++)
            {
                if (n > 0) sbReused.Append(delimiter);
                string s = del(items[n]);
                if (!string.IsNullOrEmpty(s)) { sbReused.Append(s); }
            }
            return sbReused.ToString();
        }

        public static string BuildList<T>(this List<T> items, Func<T, string> del)
        {
            if (items == null) return string.Empty;
            StringBuilder sb = new StringBuilder();
            return items.BuildList(sb, ", ", del);
        }

        public static string BuildList<T>(this IEnumerable<T> items, Func<T, string> del)
             where T : class
        {
            return items.BuildList(", ", del);
        }

        public static string BuildList<T>(this IEnumerable<T> items, string delimiter, Func<T, string> del)
        {
            if (items == null) return string.Empty;
            StringBuilder sb = new StringBuilder(1024);
            int n = 0;
            foreach (T item in items)
            {
                if (item == null) continue;
                if (n > 0) sb.Append(delimiter);
                string s = del(item);
                if (!string.IsNullOrEmpty(s)) { sb.Append(s); n++; }
            }
            return sb.ToString();
        }

        public static bool ContainsIgnoreCase(this List<string> list, string s)
        {
            for (int j = 0; j < list.Count; j++)
            {
                if (list[j].Equals(s, StringComparison.OrdinalIgnoreCase))
                    return true;
            }
            return false;
        }

        public static T FindFirst<T>(this List<T> list, Func<T, bool> func)
             where T : class
        {
            for (int n = 0; n < list.Count; n++)
            {
                if (func(list[n]))
                    return list[n];
            }
            return null;
        }

        public static T2 FindFirst<T1, T2>(this List<T1> list, out T1 item, Func<T1, T2> func)
            where T1 : class
            where T2 : class
        {
            for (int n = 0; n < list.Count; n++)
            {
                T2 found = func(list[n]);

                if (found != null)
                {
                    item = list[n];
                    return found;
                }
            }

            item = null;
            return null;
        }

        public static List<T> FindMatching<T>(this List<T> list, Func<T, bool> func)
             where T : class
        {
            List<T> results = new List<T>();
            for (int n = 0; n < list.Count; n++)
            {
                if (func(list[n]))
                    results.Add(list[n]);
            }
            return results;
        }

        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> items, Action<T> action)
        {
            foreach (T item in items)
                action(item);

            return items;
        }

        public static IEnumerable ForEach<T>(this IEnumerable items, Action<T> action)
             where T : class
        {
            foreach (T item in items)
                action(item);

            return items;
        }

        public static T[] ForEachAttribute<T>(this T[] attrs, Action<T> del)
        {
            if (attrs != null && attrs.Length > 0)
            {
                for (int n = 0; n < attrs.Length; n++)
                {
                    T attr = attrs[n];
                    del(attr);
                }
            }

            return attrs;
        }

        public static ObservableCollection<T> ForEachBackwards<T>(this ObservableCollection<T> list, Action<T> action)
        {
            for (int n = list.Count - 1; n >= 0; n--)
                action(list[n]);

            return list;
        }

        public static ObservableCollection<T> RemoveWhere<T>(this ObservableCollection<T> list, Func<T, bool> test)
        {
            if (list == null)
                return null;

            for (int n = list.Count - 1; n >= 0; n--)
                if (test(list[n])) list.RemoveAt(n);

            return list;
        }

        public static List<T> ForEachBackwards<T>(this List<T> list, Action<T> action)
        {
            for (int n = list.Count - 1; n >= 0; n--)
                action(list[n]);

            return list;
        }

        public static void ForEachEnumValue<T>(this T max, Action<T> action)
             where T : struct
        {
            // Check performance hit we could take for doing this kind of type casting/conversion...
            //Type testing = Enum.GetUnderlyingType(typeof(T));

            int maxVal = (int)Convert.ChangeType(max, typeof(T), CultureInfo.CurrentCulture);

            for (int n = 0; n < maxVal; n++)
            {
                T item = (T)Enum.ToObject(typeof(T), n);
                action(item);
            }
        }

        public static IEnumerable<T> ForEachIfNotNull<T>(this IEnumerable<T> items, Action<T> action)
        {
            if (items == null)
                return null;

            foreach (T item in items)
                action(item);

            return items;
        }

        public static void ForEachMatch<T>(this IEnumerable<T> items, Func<T, bool> match, Action<T> action)
        {
            foreach (T item in items)
            {
                if (match(item))
                    action(item);
            }
        }

        public static T Grab<T>(this IList items)
        {
            if (items == null)
                return default(T);

            IList<T> inner = items[0] as IList<T>;

            if (inner == null)
                return (T)items[0];

            return inner[0];
        }

        public static bool MoveDown<T>(this List<T> list, Func<T, bool> Match)
             where T : class
        {
            T temp = null;
            int n = 0;

            for (; n < list.Count - 1; n++)
            {
                if (Match(list[n]))
                {
                    temp = list[n];
                    list.RemoveAt(n);
                    break;
                }
            }
            if (temp != null) { list.Insert(n + 1, temp); }

            return (temp != null);
        }

        public static bool MoveUp<T>(this List<T> list, Func<T, bool> Match)
             where T : class
        {
            T temp = null;
            int n = 1;

            for (; n < list.Count; n++)
            {
                if (Match(list[n]))
                {
                    temp = list[n];
                    list.RemoveAt(n);
                    break;
                }
            }
            if (temp != null) { list.Insert(n - 1, temp); }

            return (temp != null);
        }

        public static T2[] PlaceInArray<T1, T2>(this List<T1> list, Func<T1, T2> del)
             where T1 : class
        {
            T2[] results = new T2[list.Count];

            for (int n = 0; n < list.Count; n++)
            {
                T2 res = del(list[n]);
                results[n] = res;
            }

            return results;
        }

        public static void RemoveDuplicates<T>(this List<T> items)
             where T : class
        {
            for (int n = 0; n < items.Count; n++)
            {
                for (int j = items.Count - 1; j > n; j--)
                {
                    if (items[n] == items[j])
                        items.RemoveAt(j);
                }
            }
        }

        public static void Swap<T>(this List<T> list, int n1, int n2)
             where T : class
        {
            T temp = list[n1];
            list[n1] = list[n2];
            list[n2] = temp;
        }

        public static List<T> Take<T>(this List<T> items, Func<T, bool> action)
        {
            List<T> results = new List<T>();

            for (int n = 0; n < items.Count; n++)
            {
                if (action(items[n]))
                    results.Add(items[n]);
            }

            return results;
        }

        public static List<T> Take<T>(this IEnumerable items, Func<T, bool> action)
        {
            List<T> results = new List<T>();

            foreach (T item in items)
            {
                if (action(item))
                    results.Add(item);
            }

            return results;
        }

        public static bool IsInSet<T>(this T item, params T[] setOfItems)
        {
            for (int n = 0; n < setOfItems.Length; n++)
            {
                if (item.Equals(setOfItems[n]))
                    return true;
            }
            return false;
        }

    }
}